Associations

✒️ 2025-05-26 15:09 내용 수정

Node.js 교과서 개정 3판 내용 정리


Associations 종류

// A는 source model, B는 target model
A.hasOne(B, { /* option */}); // A는 B 1개를 가짐
A.belongsTo(B, { /* option */}); // A는 B에 의존
A.hasMany(B, { /* option */}); // A는 B 여러 개를 가짐
A.belongsToMany(B, { through: 'C', /* option */}); // A는 C 교차 테이블을 통해 B들에 의존
Association 설명
A.hasOne(B) target model B에 정의된 Foreign key로 연결된 1:1 관계
A.hasMany(B) target model B에 정의된 Foreign key로 연결된 A와 B 간의 1:N 관계
A.belongsToOne(B) source model A에 정의된 Foreign key로 연결된 1대1 관계
A.belongsToMany(B, { through: 'C'}) A와 B의 키를 Foreign key로 가진 연결 테이블 C를 사용한 A와 B 간의 N:M 관계
  1. 1:1 관계
    • foreignKey를 따로 옵션에 설정하지 않는다면 Sequelize에선 기본으로 B model에 A model에 대한 Foreign key가 있는 것으로 파악한다.
// 1번째 : hasOne에 설정
A.hasOne(B, {
	foreignKey: 'aId'
});

B.belongsTo(A);

// 2번째 : belongsTo에 설정
A.hasOne(B);

B.belongsTo(A, {
	foreignKey: 'aId'
});
  1. 1:N 관계
// 1번째 : hasMany에 설정
A.hasMany(B, {
	foreignKey: 'aId'
});

B.belongsTo(A);

// 2번째 : belongsTo에 설정
A.hasMany(B);

B.belongsTo(A, {
	foreignKey: 'aId'
});
  1. N:M 관계
    • Sequelize에선 N:M 관계에 필요한 연결 테이블(아래에선 C)를 자동으로 생성한다.
    • 참고 자료 : Sequelize Many-To-Many relationships
    • 상세한 설명은 아직 구현 시도를 해보지 않아 이해가 어려워 기본 구성만 작성했다.
A.belongsToMany(B, { through : 'C' });
B.belongsToMany(A, { through : 'C' });

연결된 테이블간의 query

A.hasOne(B);
B.belongsTo(A);

const test = await A.findOne({
	where : {
		name : 'test'
	},
	include: B
});

Association 설정

  1. index.js에 associate 설정을 추가한다.
    • npm sequelize init 수행 후 자동 생성되는 index.js에서 일부 옵션을 수정한 형태다.
    • model 폴더에 model 파일만 넣어야 하며, 미완성 모델이 있으면 에러가 발생할 수 있다.
'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) { // Sequelize 객체 생성
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => { // 숨김 파일, index.js, js 확장자가 아닌 파일 필터링
    return (
      file.indexOf('.') !== 0 &&
      file !== basename &&
      file.slice(-3) === '.js' &&
      file.indexOf('.test.js') === -1
    );
  })
  .forEach(file => { // 해당 파일의 Model을 불러온 후 init
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => { // associate 호출
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
  1. Model 파일에 associate 함수 내에 테이블간 관계를 지정한다.
    • 실습에선 Blog 테이블과 User 테이블을 1(User):N(Blog) 관계로 설정했다.
// User 테이블 모델
'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    static associate(models) {
      this.hasMany(models.Blog, { // Blog 테이블과의 관계
        sourceKey: 'id',
        foreignKey: 'user_id',
      })
    }
  }
  User.init({
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER
    },
    username: DataTypes.STRING,
    password: DataTypes.STRING
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};
// Blog 테이블 모델
'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class Blog extends Model {
    static associate(models) {
      this.belongsTo(models.User, { // User 테이블과의 관계
        targetKey: 'id',
        foreignKey: 'user_id',
      })
    }
  }
  Blog.init({
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER
    },
    user_id : DataTypes.INTEGER,
    title: DataTypes.STRING,
    content: DataTypes.STRING
  }, {
    sequelize,
    modelName: 'Blog',
  });
  return Blog;
};
  1. query문 작성 시 include 옵션으로 join을 수행한다.
const result = await Blog.findAndCountAll({
	where: {
		user_id: inputId
	},
	include: [{
		model: User,
		attributes: ['username']
	}],
	offset,
	limit
});

const pages = result.count;
const data = result.rows;